/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* @author: ptoofani
* Created: June 2004
*/
package org.python.pydev.editor.actions;
import java.util.HashSet;
import java.util.List;
import org.eclipse.jface.action.IAction;
import org.python.pydev.core.IIndentPrefs;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.editor.autoedit.DefaultIndentPrefs;
import com.aptana.shared_core.string.FastStringBuffer;
import com.aptana.shared_core.structure.Tuple;
/**
* Removes a comment block. Comment blocks are slightly different than regular
* comments created in that they provide a distinguishing element at the
* beginning and end as a separator. In this case, it is a string of
* <code>=======</code> symbols to strongly differentiate this comment block.
*
* This will handle regular comment blocks as well by removing the # token at
* the head of each line, but will also remove the block separators if they are
* present.
*
* Changes in 1.2.7: if any line of a block comment is selected, all 'adjacent' lines
* will have its comment removed (without the need to select the whole lines
*
* @author Parhaum Toofanian
* @author Fabio Zadrozny
*/
public class PyRemoveBlockComment extends PyAddBlockComment {
/**
* Grabs the selection information and performs the action.
*/
public void run(IAction action) {
try {
if (!canModifyEditor()) {
return;
}
// Select from text editor
PySelection ps = new PySelection(getTextEditor());
// Put cursor at the first area of the selection
getTextEditor().selectAndReveal(perform(ps), 0);
} catch (Exception e) {
beep(e);
}
}
/**
* Performs the action with a given PySelection
*
* @param ps Given PySelection
* @return boolean The success or failure of the action
*/
public int perform(PySelection ps) {
// What we'll be replacing the selected text with
FastStringBuffer strbuf = new FastStringBuffer();
try {
//discover 1st line that starts the block comment
int i;
int startLineIndex = getStartIndex(ps);
int endLineIndex = getEndIndex(ps);
if (startLineIndex == -1 || endLineIndex == -1) {
if (startLineIndex == -1 && endLineIndex == -1) {
return -1;
} else if (startLineIndex == -1) {
startLineIndex = endLineIndex;
} else {
endLineIndex = startLineIndex;
}
}
// For each line, uncomment it
for (i = startLineIndex; i <= endLineIndex; i++) {
boolean addDelim = true;
String spacesBefore = "";
String line = ps.getLine(i);
int lineLen = line.length();
for (int j = 0; j < lineLen; j++) {
char c = line.charAt(j);
if (c == '#') {
//ok, it starts with # (so, remove the whitespaces before it)
if (j > 0) {
spacesBefore = line.substring(0, j);
}
line = line.substring(j);
break;
} else {
if (!Character.isWhitespace(c)) {
break;
}
}
}
if (line.startsWith("#")) {
line = line.substring(1);
}
//get the chars used in block-comments
AbstractBlockCommentAction[] acts = new AbstractBlockCommentAction[] { new PyAddSingleBlockComment(),
new PyAddBlockComment() };
HashSet<Character> chars = new HashSet<Character>();
for (int j = 0; j < acts.length; j++) {
AbstractBlockCommentAction action = acts[j];
chars.add(action.getColsAndChar().o2);
}
if (line.length() > 0) {
boolean removedChar = false;
char lastChar = '\0';
for (int j = 0; j < line.length(); j++) {
lastChar = line.charAt(j);
if (!chars.contains(lastChar)) {
break;
} else {
removedChar = true;
line = line.substring(1);
j--;
}
}
if (line.length() == 0 && removedChar) {
addDelim = false;
}
if (removedChar && lastChar == ' ') {
line = line.substring(1);
}
}
if (addDelim) {
strbuf.append(spacesBefore);
strbuf.append(line);
String lineDelimiter = ps.getDoc().getLineDelimiter(i);
if (lineDelimiter != null) {
strbuf.append(lineDelimiter);
}
}
}
//Ok, at this point things should be correct, but make sure than on uncomment,
//the code goes to a proper indent position (remove spaces we may have added when creating a block).
String string = strbuf.toString();
List<String> lines = StringUtils.splitInLines(string);
Tuple<Integer, String> posAndLine = new Tuple<Integer, String>(-1, "");
for (String line : lines) {
int firstCharPosition = PySelection.getFirstCharPosition(line);
if (firstCharPosition < posAndLine.o1 || posAndLine.o1 < 0) {
posAndLine.o1 = firstCharPosition;
posAndLine.o2 = line;
}
}
if (posAndLine.o1 > 0) {
final String sub = posAndLine.o2.substring(0, posAndLine.o1);
if (sub.endsWith(" ")) { //If it ends with a tab, we won't change anything (only spaces are removed -- which we may have introduced)
boolean allEqual = true;
for (String line : lines) {
if (!line.startsWith(sub)) {
allEqual = false;
break;
}
}
if (allEqual) {
if (sub.startsWith("\t")) {
//Tabs based indent: remove any ending spaces (and at this point we know a string ends with a space)
int j;
for (j = sub.length() - 1; j >= 0; j--) {
char c = sub.charAt(j);
if (c != ' ') {
j++;
break;
}
}
String newSub = sub.substring(0, j);
strbuf.clear();
for (String line : lines) {
strbuf.append(newSub);
strbuf.append(line.substring(sub.length()));
}
} else {
IIndentPrefs indentPrefs;
if (targetEditor instanceof PyEdit) {
PyEdit pyEdit = (PyEdit) targetEditor;
indentPrefs = pyEdit.getIndentPrefs();
} else {
indentPrefs = DefaultIndentPrefs.get();
}
String indentationString = indentPrefs.getIndentationString();
int subLen = sub.length();
int indentLen = indentationString.length();
int mod = subLen % indentLen;
if (mod != 0) {
String substring = sub.substring(subLen - mod, subLen);
boolean onlyWhitespaces = true;
for (int k = 0; k < substring.length(); k++) {
if (substring.charAt(k) != ' ') {
onlyWhitespaces = false;
break;
}
}
if (onlyWhitespaces) {
String newSub = sub.substring(0, subLen - mod);
strbuf.clear();
for (String line : lines) {
strbuf.append(newSub);
strbuf.append(line.substring(sub.length()));
}
}
}
}
}
}
}
// Replace the text with the modified information
int startLineOffset = ps.getLineOffset(startLineIndex);
int endLineOffset = ps.getEndLineOffset(endLineIndex);
String endLineDelimiter = ps.getDoc().getLineDelimiter(endLineIndex);
if (endLineDelimiter != null) {
endLineOffset += endLineDelimiter.length();
}
String str = strbuf.toString();
ps.getDoc().replace(startLineOffset, endLineOffset - startLineOffset, str);
return startLineOffset + str.length();
} catch (Exception e) {
beep(e);
}
// In event of problems, return false
return -1;
}
private int getEndIndex(PySelection ps) {
int endLineIndex = -1;
int i = ps.getEndLineIndex();
while (true) {
String line = ps.getLine(i);
if (PySelection.isCommentLine(line)) {
endLineIndex = i;
} else {
break;
}
i++;
}
return endLineIndex;
}
private int getStartIndex(PySelection ps) {
int startLineIndex = -1;
int i = ps.getStartLineIndex();
while (true) {
String line = ps.getLine(i);
if (PySelection.isCommentLine(line)) {
startLineIndex = i;
} else {
break;
}
i--;
}
return startLineIndex;
}
}